/*
 * Copyright (C) Francisco Morero Peyrona. All rights reserved.
 *
 * This software is published under the terms of Open Source
 * License version 1.1, a copy of which has been included with this
 * distribution in the License.txt file.
 */

package agents;

import java.sql.*;
import java.util.*;
import java.util.Vector;

import jdbcmanager.app.App;
import jdbcmanager.dbtools.datatypes.*;

/**
 * Agent Meta Data for Oracle RDBMS dirver.
 *  
 * @author Francisco Morero Peyrona
 */
public class AgentMetaDataOracle extends AgentMetaDataDefault
{
    private TableColumnTypeDefinition[] supportedColumnTypes; // To cache the vector of supported types.
                                                        // See this.getSupportedColumnTypes()
    
    public DbEntityDescriptor[] getTableNames( String sCatalog, String sSchema, String[] asType )
    {
       if( sSchema == null )
          sSchema = "dbo";

       return super.getTables( sCatalog, sSchema, null, asType );
    }
   
    public TableColumnTypeDefinition[] getSupportedColumnTypes()
    {
        if( this.supportedColumnTypes == null )
            this.supportedColumnTypes = buildSupportedColumnTypes();
        
        return this.supportedColumnTypes;
    }
   
    /**
     * Builds the array with all types supported by this database 
     */
    private TableColumnTypeDefinition[] buildSupportedColumnTypes()
       {
        Properties oraTypes = new Properties();
        oraTypes.setProperty( "2003", "ARRAY");	// All oracle types. Hardcoded because the
		oraTypes.setProperty( "-13", "BFILE");	// driver doesn't provide all types (?)
		oraTypes.setProperty( "-5", "BIGINT");
		oraTypes.setProperty( "-7", "BIT");
		oraTypes.setProperty( "2004", "BLOB");
		oraTypes.setProperty( "16", "BOOLEAN");
		oraTypes.setProperty( "1", "CHAR");
		oraTypes.setProperty( "2005", "CLOB");
		oraTypes.setProperty( "-10", "CURSOR");
		oraTypes.setProperty( "70", "DATALINK");
		oraTypes.setProperty( "91", "DATE");
		oraTypes.setProperty( "3", "DECIMAL");
		oraTypes.setProperty( "8", "DOUBLE");
		oraTypes.setProperty( "999", "FIXED_CHAR");
		oraTypes.setProperty( "6", "FLOAT");
		oraTypes.setProperty( "4", "INTEGER");
		oraTypes.setProperty( "-104", "INTERVALDS");
		oraTypes.setProperty( "-103", "INTERVALYM");
		oraTypes.setProperty( "2000", "JAVA_OBJECT");
		oraTypes.setProperty( "2008", "JAVA_STRUCT");
		oraTypes.setProperty( "-4", "LONGVARBINARY");
		oraTypes.setProperty( "-1", "LONGVARCHAR");
		oraTypes.setProperty( "0", "NULL");
		oraTypes.setProperty( "2", "NUMBER");
		oraTypes.setProperty( "2007", "OPAQUE");
		oraTypes.setProperty( "1111", "OTHER");
		oraTypes.setProperty( "-14", "PLSQL_INDEX_TABL");
		oraTypes.setProperty( "-2", "RAW");
		oraTypes.setProperty( "7", "REAL");
		oraTypes.setProperty( "2006", "REF");
		oraTypes.setProperty( "-8", "ROWID");
		oraTypes.setProperty( "5", "SMALLINT");
		oraTypes.setProperty( "2002", "STRUCT");
		oraTypes.setProperty( "92", "TIME");
		oraTypes.setProperty( "93", "TIMESTAMP");
		oraTypes.setProperty( "-102", "TIMESTAMPLTZ");
		oraTypes.setProperty( "-100", "TIMESTAMPNS");
		oraTypes.setProperty( "-101", "TIMESTAMPTZ");
		oraTypes.setProperty( "-6", "TINYINT");
		oraTypes.setProperty( "-3", "VARBINARY");
		oraTypes.setProperty( "12", "VARCHAR");
          
        TableColumnTypeDefinition[] result = null;
        TableColumnTypeDefinition tctd = null;
        Vector v = new Vector();
        try
        {
             ResultSet rs = getTypeInfo();
             
             while( rs.next() )
             {
                tctd = new TableColumnTypeDefinition();
                
                tctd.setName(			rs.getString(	"TYPE_NAME" ));		// The standard SQL type name
                tctd.setType( 			rs.getInt(		"DATA_TYPE" )); 	// The SQL type ID (java.sql.Types)
                tctd.setNullabe( 		rs.getShort( 	"NULLABLE" ) == DatabaseMetaData.typeNullable);
                tctd.setCaseSensitive(	rs.getBoolean(	"CASE_SENSITIVE" ));
                tctd.setUnsigned(		rs.getBoolean(	"UNSIGNED_ATTRIBUTE" ));
                tctd.setMoneyValue(		rs.getBoolean(	"FIXED_PREC_SCALE" ));
                tctd.setAutoIncrement(	rs.getBoolean(	"AUTO_INCREMENT" ));
                tctd.setLocalName( 		rs.getString( 	"LOCAL_TYPE_NAME" ));    // The name that the DBMS uses (may be null)
                tctd.setMinScale(		rs.getInt(		"MINIMUM_SCALE" ));
                tctd.setMaxScale(		rs.getInt(		"MAXIMUM_SCALE" ));
                if( !"BLOB".equalsIgnoreCase( tctd.getName()) && !"CLOB".equalsIgnoreCase( tctd.getName()) )
                    tctd.setPrecision( rs.getInt( 		"PRECISION" ));	// Quick fix to avoid exception
                v.add( tctd );
                
                // Remove the type of the list, because we have more info here
                oraTypes.remove( String.valueOf( tctd.getType()));
             }
             rs.close();
        }
        catch( SQLException exc )
        {
             App.showException( exc, "Error retrieving data types" );
        }
          
        // Now we had the types that the driver didn't return
        Enumeration keys = oraTypes.keys();
        while ( keys.hasMoreElements())
        {
            String key = (String ) keys.nextElement();
            tctd = new TableColumnTypeDefinition();
            tctd.setName(		oraTypes.getProperty( key));
            tctd.setLocalName(	oraTypes.getProperty( key));
            tctd.setType( 		Integer.parseInt( key));
            // I don't know about the other attributes.  Better than nothing.
            // TODO: check if we can get more info from the driver 
            v.add( tctd );
        }
        result = new TableColumnTypeDefinition[ v.size() ];
        v.copyInto( result );         
        return result;
       }  

	/**
	 * @see jdbcmanager.dbtools.agentwise.AgentMetaData#mapType(int)
	 */
	public int mapType(int columnType)
	{
/*
 * COLUMN TYPES CORRESPONDENCE TABLE
 * I generated it with replection of the 2 classes: OracleTypes and java.sql.Types
 * In the left side are the Oracle column types with their value
 * and in the rigth side the standard jdbc types.
 * (Oracle ojdbc14.jar and JAVA 1.4.2/JAVA 5 (?) For other version could change)
 * 
 * It would be posible to build this 2 arrays authomatically, with reflection, but the
 * mapping for not-matching values has to be defined manually anyway.
 *
	Oracle Column Types			jdbc standard types
	----------------------------------------------------
	ARRAY 	 		2003		ARRAY 	 		2003         
	BFILE 	 		-13                  		
	BIGINT 	 		-5          BIGINT 	 		-5           
	BINARY 	 		-2          BINARY 	 		-2           
	BIT 	 		-7          BIT 	 		-7           
	BLOB 	 		2004        BLOB 	 		2004         
	BOOLEAN 	 	16          BOOLEAN 	 	16       
	CHAR 	 		1           CHAR 	 		1            
	CLOB 	 		2005        CLOB 	 		2005         
	CURSOR 	 		-10                  		
	DATALINK 	 	70          DATALINK 		70       
	DATE 	 		91          DATE 	 		91           
	DECIMAL 	 	3           DECIMAL 		3        
								DISTINCT 		2001    
	DOUBLE 	 		8           DOUBLE 	 		8            
	FIXED_CHAR 	 	999                  		
	FLOAT 	 		6           FLOAT 	 		6            
	INTEGER 	 	4           INTEGER 	 	4        
	INTERVALDS 	 	-104         
	INTERVALYM 	 	-103         
	JAVA_OBJECT 	2000        JAVA_OBJECT 	2000 
	JAVA_STRUCT 	2008         
	LONGVARBINARY 	-4          LONGVARBINARY 	-4   
	LONGVARCHAR 	-1          LONGVARCHAR 	-1   
	NULL 			0           NULL 	 		0            
	NUMBER 			2                    		
	NUMERIC 		2           NUMERIC 		2        
	OPAQUE 			2007                 		
	OTHER 			1111        OTHER 	 		1111                            
	PLSQL_INDEX_TABLE -14                		
	RAW 	 		-2                   		
	REAL 	 		7           REAL 	 		7                               
	REF 	 		2006        REF 	 		2006                            
	ROWID 	 		-8                   		
	SMALLINT 		5           SMALLINT 		5                           
	STRUCT 	 		2002        STRUCT 	 		2002                            
	TIME 	 		92          TIME 	 		92                              
	TIMESTAMP 	 	93          TIMESTAMP 	 	93                          
	TIMESTAMPLTZ 	-102                            
	TIMESTAMPNS 	-100        
	TIMESTAMPTZ 	-101		
	TINYINT 	 	-6			TINYINT 	 	-6      
	VARBINARY 	 	-3          VARBINARY 	 	-3      
	VARCHAR 	 	12          VARCHAR 	 	12
*/      

	    switch ( columnType ) {
	    	case 2003: 			// all the next types already exist in the jdbc standard
			case -5  :
			case -2  :
			case -7  :
			case 2004:
			case 16  :
			case 1   :
			case 2005:
			case 70  :
			case 91  :
			case 3   :
			case 8   :
			case 6   :
			case 4   :
			case 2000:
			case -4  :
			case -1  :
			case 0   :
			case 2   :
			case 1111:
			case 7   :
			case 2006:
			case 5   :
			case 2002:
			case 92  :
			case 93  :
			case -6	 :
			case -3  :
			case 12  :
			    return columnType;

			// The next ones I map how I think could work
			case 999 : // FIXED_CHAR
			    return Types.CHAR;
			case -8  : // ROWID
			    return Types.VARCHAR;

			// And the next ones I have no idea
			case -13 : // BFILE
			case -10 : // CURSOR
			case -104: // INTERVALDS
			case -103: // INTERVALYM
			case 2008: // JAVA_STRUCT 	
			case 2007: // OPAQUE 			
			case -14 : // PLSQL_INDEX_TABLE
			case -102: // TIMESTAMPLTZ
			case -100: // TIMESTAMPNS
			case -101: // TIMESTAMPTZ
			    return Types.OTHER;

			default: // TODO: handle error better
		        System.out.println("Unknow type: " + columnType);
			    return Types.OTHER;
        }
	}
}